import socket
import threading
import cv2
import time
import numpy as np
import struct

from pynput import mouse
from multiprocessing.managers import BaseManager

HeightShow = 800
WidthShow = 1280

Height = 600
Width = 960

CastPortMouse = 6019
# CastPortMouse2 = 6019
CastPort = 6117
CastIp = "192.168.3.112"
# CastIp = "192.168.137.213"
# CastIp = '127.0.0.1'
Quality = "80"


class MyManager(BaseManager):
    pass


class VideoShow:
    def __init__(self, host_port, host_ip, host_port_pyautogui, timeout=10) -> None:
        super().__init__()

        self.events = []
        self.lock = threading.Lock()
        self.lock_image = threading.Lock()
        self.image = np.zeros((Height, Width, 1), dtype=np.uint8)

        self.timeout = timeout
        self.host_port = host_port
        self.host_ip = host_ip
        self.host_port_pyautogui = host_port_pyautogui
        self.window_name = "my vnc"

        # MyManager.register("pyautogui")
        # m = MyManager(address=(self.host_ip, self.host_port_pyautogui), authkey=b"abracadabra")

        # # ts = time.time()
        # # while not isPortInUse(Port):
        # #     time.sleep(0.2)
        # #     if time.time() - ts > 10:
        # #         raise Exception("loading pyautogui timeout")

        # while True:
        #     ts = time.time()
        #     try:
        #         m.connect()
        #     except Exception as e:
        #         if time.time() - ts > 10:
        #             raise Exception("loading pyautogui timeout")
        #         time.sleep(0.1)
        #         continue
        #     break

        # self.pyautogui = m.pyautogui()

        self.mouse_thread = threading.Thread(target=self.mouseThread, args=[self.host_ip, host_port_pyautogui], daemon=True)
        self.mouse_thread.start()

    def on_click(self, x, y, button, pressed):
        # print(x, y, button, pressed)
        if button == mouse.Button.left:
            if pressed:
                print("鼠标左键按下")
            else:
                print("鼠标左键释放")

    def on_scroll(self, x, y, dx, dy):
        pass  # 可以在这里处理滚轮事件

    def mouseThread(self, host, port):
        while True:
            try:
                self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self.client_socket.connect((host, port))

                while True:
                    time.sleep(1)
                    if self.client_socket is None:
                        break

            except Exception as e:
                print(e)

        self.client_socket.close()

    def sendMouseEvent(self, event, x, y, flags):
        try:
            byte_array = bytearray()
            byte_array += event.to_bytes(4, "little")
            byte_array += x.to_bytes(4, "little")
            byte_array += y.to_bytes(4, "little")
            byte_array += flags.to_bytes(4, "little")
            self.client_socket.send(byte_array)
        except Exception as e:
            print(e)
            self.client_socket = None

    def mouse_callback(self, event, x, y, flags, param):
        import datetime

        now = datetime.datetime.now()

        if event:
            print(event, x, y, flags, param, now)
            # self.sendMouseEvent(event, x, y, flags)

        # if event == cv2.EVENT_LBUTTONDOWN:
        #     self.pyautogui.click(255, 93)

        if event == cv2.EVENT_LBUTTONDOWN:
            self.sendMouseEvent(event, x, y, flags)

        #     # self.pyautogui.moveTo(x, y)
        #     # self.pyautogui.mouseDown(button="left")

        # elif event == cv2.EVENT_MOUSEMOVE:
        #     if flags > 0:
        #         self.sendMouseEvent(event, x, y, flags)

        elif event == cv2.EVENT_LBUTTONUP:
            self.sendMouseEvent(event, x, y, flags)

        # self.pyautogui.moveTo(x, y)
        # self.pyautogui.mouseUp(button="left")

    def show(self):
        cv2.namedWindow(self.window_name)
        cv2.setMouseCallback(self.window_name, self.mouse_callback)

        while True:
            try:
                client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                # client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                client.connect((self.host_ip, self.host_port))

                fps_count = 0
                fps = 0
                ts = time.time()
                quality_length = len(Quality)

                image = np.zeros((Height, Width, 3), dtype=np.uint8)

                try:
                    client.send(quality_length.to_bytes(4, "little"))
                    client.send(Quality.encode("utf-8"))

                    flow_ts = time.time()
                    length_count = []
                    frame_time_s = time.time()
                    while True:
                        # client.send(quality_length.to_bytes(4, 'little'))
                        # client.send(Quality.encode('utf-8'))

                        length = client.recv(4)
                        if len(length) <= 0:
                            break
                        length = int.from_bytes(length, "little")
                        # print("length", length)

                        remain = length
                        byte_data = client.recv(remain)
                        remain = length - len(byte_data)
                        while remain > 0:
                            byte_data += client.recv(remain)
                            remain = length - len(byte_data)

                        length_count.append(length + 4)
                        flow_te = time.time()
                        if flow_te - flow_ts > 1:
                            print(f"流量: {np.sum(length_count) / (flow_te - flow_ts) / 1024:.3f} kbps")
                            print(length_count)
                            flow_ts = time.time()
                            length_count = []

                        # region 解包
                        size_of_int = 4
                        num_ints = 5
                        int_format = "I"  # 'I' 表示无符号整数
                        i = 0
                        jpeg_count = 0
                        while i < length:
                            head_data = byte_data[i : i + size_of_int * num_ints]
                            i += size_of_int * num_ints
                            left, top, width, height, jpeg_len = struct.unpack(int_format * num_ints, head_data)

                            data = np.frombuffer(byte_data[i : i + jpeg_len], dtype=np.uint8)
                            i += jpeg_len

                            # roi = cv2.imdecode(data, cv2.IMREAD_COLOR)
                            # image[top : top + height, left : left + width] = roi
                            image[top : top + height, left : left + width] = cv2.imdecode(data, cv2.IMREAD_COLOR)

                            jpeg_count += 1
                        # endregion

                        # print('bytes', len(bytes))

                        # remain = jpeg_len

                        # byte_data = client.recv(remain)
                        # remain = length - len(byte_data)
                        # # print('remain', remain)
                        # while remain > 0:
                        #     byte_data += client.recv(remain)
                        #     remain = length - len(byte_data)
                        #     # print('remain', remain)

                        # # print('bytes', len(bytes))

                        fps_count += 1
                        if fps_count >= 30:
                            t = time.time() - ts
                            ts = time.time()
                            fps = int(round(fps_count / t, 0))
                            # print("fps:%d" % (fps))
                            fps_count = 0

                        # continue

                        # 原图接收
                        # data = np.frombuffer(byte_data, dtype=np.uint8)
                        # image = cv2.imdecode(data, cv2.IMREAD_GRAYSCALE)
                        # image = cv2.imdecode(data, cv2.IMREAD_COLOR)
                        # image = np.reshape(data, (Height, Width, 4))

                        show_img = np.copy(image)
                        # show_img = cv2.resize(image, (WidthShow, HeightShow), cv2.INTER_AREA)
                        cv2.putText(show_img, f"FPS:{fps}", (30, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (20, 220, 20), 2)
                        # cv2.putText(show_img, f"jpeg count:{jpeg_count}", (30, 80), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (20, 220, 20), 2)
                        cv2.imshow(self.window_name, show_img)
                        cv2.waitKey(1)

                        frame_time_s = time.time()
                except Exception as e:
                    print(e)

                # client.shutdown(socket.SHUT_RDWR)
                client.close()
            except Exception as e:
                print(e)

            time.sleep(2)


def showTest():
    video_show = VideoShow(CastPort, CastIp, CastPortMouse)
    video_show.show()


if __name__ == "__main__":
    showTest()
